#ifndef __FRAMEWORK_H__
#define __FRAMEWORK_H__

// MACROS
#define glClearColor3v(f) glClearColor((f)[0], (f)[1], (f)[2], 0.0)

#include <GL/glut.h>
#include <string>
#include "./pipeline/pipeline.h"

class IFrameworkCallbacks{
	public:
		IFrameworkCallbacks(){}
		virtual ~IFrameworkCallbacks(){}


		virtual void init(){}
		virtual void display()=0;
		virtual void timer(int value){}
		virtual void keyboard(unsigned char key,int x ,int y)=0;
		virtual void specKeyboard(int key,int x ,int y)=0;
};

class Framework{
	public:
		Pipeline pipeline;
	private:
		IFrameworkCallbacks* callbacks;
		int windowHeight;
		int windowWidth;
		int windowX;
		int windowY;
		int window;
		std::string caption;
		bool timerSet;
		unsigned int msecTimer;

	public:
		~Framework(){}

		static Framework& instance(){
			static Framework framework;
			return framework;
		}

		static void display(){
			///
			glClear(GL_COLOR_BUFFER_BIT);
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			///

			Framework::instance().getCallbacks()->display();

			///
			glutSwapBuffers();
			///
		}

		static void reshape(int w, int h){
			glViewport (0, 0, (GLsizei) w, (GLsizei) h);
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluOrtho2D(0.0, (GLdouble)w, (GLdouble)h, 0.0);
		}

		static void keyboard(unsigned char key,int x,int y){
			Framework::instance().getCallbacks()->keyboard(key,x,y);
		}

		static void specialFunc(int key,int x ,int y){
			Framework::instance().getCallbacks()->specKeyboard(key,x,y);
		}

		static void timer(int value){
			Framework& fr = Framework::instance();
			fr.getCallbacks()->timer(value);
			if(fr.isTimerSet())
				glutTimerFunc(fr.getTimerMsec(),Framework::timer,1);
		}

		bool isTimerSet(){
			return timerSet;
		}

		unsigned int getTimerMsec(){
			return msecTimer;
		}


		void setWindowDimensions(int windowHeigth,int windowWidth,int windowX,int windowY){
			this->windowHeight = windowHeigth;
			this->windowWidth = windowWidth;
			this->windowX = windowX;
			this->windowY = windowY;
		}

		void setClearColor(float r,float g,float b){
			glClearColor(r,g,b,0.0);
		}

		void setCaption(const std::string& caption){
			this->caption = caption;
		}

		void setTimer(unsigned int msec){
			this->timerSet = true;
			this->msecTimer = msec;
		}

		void unsetTimer(){
			this->timerSet = false;
		}

		void setCallbacks(IFrameworkCallbacks* callbacks){
			this->callbacks = callbacks;
		}

		void redisplay(){
			glutPostWindowRedisplay(this->window);
		}

		int getWindow(){
			return this->window;
		}

		void mainLoop(int argc,char** argv){
			glutInit(&argc, argv);
			glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
			glutInitWindowSize (windowWidth, windowHeight);
			glutInitWindowPosition (windowX, windowY);
			window = glutCreateWindow (caption.c_str());
			callbacks->init();
			glutKeyboardFunc(Framework::keyboard);
			glutDisplayFunc(Framework::display);
			glutSpecialFunc(Framework::specialFunc);
			glutReshapeFunc(Framework::reshape);
			if(isTimerSet())
				glutTimerFunc(msecTimer,Framework::timer,1);
			glutMainLoop();
		}

	private:
		Framework(){
			this->setWindowDimensions(600,600,0,0);
			this->setCaption("");
			this->unsetTimer();
		}

		Framework(const Framework& framework);
		void operator=(const Framework& framework);

		IFrameworkCallbacks* getCallbacks(){ return callbacks; }
};

#endif // __FRAMEWORK_H__
